use super::*;

pub mod acnt;
pub mod ankr;
pub mod avar;
pub mod base;
pub mod bsln;
pub mod cbdt;
pub mod cblc;
pub mod cff1;
pub mod cff2;
pub mod cmap;
pub mod colr;
pub mod cpal;
pub mod cvar;
pub mod cvt_;
pub mod ebsc;
pub mod fdsc;
pub mod feat;
pub mod fftm;
pub mod fmtx;
pub mod fond;
pub mod fpgm;
pub mod fvar;
pub mod gasp;
pub mod gcid;
pub mod gdef;
pub mod glyf;
pub mod gpos;
pub mod gsub;
pub mod gvar;
pub mod hdmx;
pub mod head;
pub mod hhea;
pub mod hmtx;
pub mod hvar;
pub mod jstf;
pub mod just;
pub mod kern;
pub mod kerx;
pub mod lcar;
pub mod loca;
pub mod ltag;
pub mod ltsh;
pub mod math;
pub mod maxp;
pub mod merg;
pub mod meta;
pub mod morx;
pub mod mvar;
pub mod name;
pub mod opbd;
pub mod os_2;
pub mod pclt;
pub mod post;
pub mod prep;
pub mod prop;
pub mod sbix;
pub mod stat;
pub mod svg_;
pub mod trak;
pub mod vdmx;
pub mod vhea;
pub mod vorg;
pub mod vvar;
pub mod xref;
pub mod zapf;

#[derive(Clone, Copy, Debug)]
pub struct TableRecord {
	pub tag: Tag,
	pub check_sum: u32,
	pub offset: u32,
	pub length: u32,
}
impl FromData for TableRecord {
	const SIZE: usize = 16;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		Some(TableRecord {
			tag: s.read::<Tag>()?,
			check_sum: s.read::<u32>()?,
			offset: s.read::<u32>()?,
			length: s.read::<u32>()?,
		})
	}
}

#[derive(Default)]
pub struct RawTables<'a> {
	pub head: Option<&'a [u8]>, // Font header
	pub maxp: Option<&'a [u8]>, // Maximum profile
	pub fmtx: Option<&'a [u8]>, // Font Metrics Table

	pub hhea: Option<&'a [u8]>, // Horizontal header
	pub hmtx: Option<&'a [u8]>, // Horizontal metrics
	pub hdmx: Option<&'a [u8]>, // Horizontal Device Metrics

	pub vhea: Option<&'a [u8]>, // Vertical header
	pub vmtx: Option<&'a [u8]>, // Vertical metrics
	pub vdmx: Option<&'a [u8]>, // Vertical Device Metrics

	pub cmap: Option<&'a [u8]>, // Character to glyph mapping
	// Font Infomation
	pub name: Option<&'a [u8]>, // Naming table
	pub os_2: Option<&'a [u8]>, // OS/2 and Windows specific metrics
	pub post: Option<&'a [u8]>, // PostScript information
	pub fdsc: Option<&'a [u8]>, // font descriptors table
	pub meta: Option<&'a [u8]>, // Metadata
	pub stat: Option<&'a [u8]>, // Style attributes
	pub dsig: Option<&'a [u8]>, // Digital Signature Table
	// Glyphs TrueType Outlines
	pub glyf: Option<&'a [u8]>, // Glyph data
	pub loca: Option<&'a [u8]>, // Index to location
	// Glyphs TrueType instructions not surport
	pub cvt_: Option<&'a [u8]>, // Control Value Table
	pub fpgm: Option<&'a [u8]>, // Font program
	pub gasp: Option<&'a [u8]>, // Grid-fitting/Scan-conversion
	pub prep: Option<&'a [u8]>, // Control Value Program
	// Glyphs CFF Outlines
	pub cff1: Option<&'a [u8]>, // Compact Font Format 1.0
	pub cff2: Option<&'a [u8]>, // Compact Font Format 2.0
	pub vorg: Option<&'a [u8]>, // Vertical Origin (optional table)
	// Glyphs SVG Outlines
	pub svg_: Option<&'a [u8]>, // The SVG (Scalable Vector Graphics) table
	// Glyphs Bitmap
	pub bdat: Option<&'a [u8]>, // Bitmap data table (TrueType only)
	pub bloc: Option<&'a [u8]>, // Bitmap location table (TrueType only)
	pub bhed: Option<&'a [u8]>, // global information about a bitmap font
	pub colr: Option<&'a [u8]>, // Color table
	pub cpal: Option<&'a [u8]>, // Color palette table
	pub cbdt: Option<&'a [u8]>, // Color bitmap data
	pub cblc: Option<&'a [u8]>, // Color bitmap location data
	pub ebdt: Option<&'a [u8]>, // Embedded bitmap data
	pub eblc: Option<&'a [u8]>, // Embedded bitmap location data
	pub ebsc: Option<&'a [u8]>, //  Embedded bitmap scaling data
	pub sbix: Option<&'a [u8]>, // Standard bitmap graphics
	// Opentype Advanced Typographic GGG
	pub base: Option<&'a [u8]>, // Baseline data
	pub gdef: Option<&'a [u8]>, // Glyph definition data
	pub gpos: Option<&'a [u8]>, // Glyph positioning data
	pub gsub: Option<&'a [u8]>, // Glyph substitution data
	pub jstf: Option<&'a [u8]>, // Justification data
	pub math: Option<&'a [u8]>, // Math layout data
	// Apple Advanced Typography AAT
	pub acnt: Option<&'a [u8]>, // accent attachment table
	pub ankr: Option<&'a [u8]>, // anchor point table
	pub bsln: Option<&'a [u8]>, // baseline table
	pub feat: Option<&'a [u8]>, // feature name table
	pub just: Option<&'a [u8]>, // justification table
	pub kern: Option<&'a [u8]>, // kerning table
	pub kerx: Option<&'a [u8]>, // extended kerning table
	pub lcar: Option<&'a [u8]>, // ligature caret table
	pub ltag: Option<&'a [u8]>, // map numeric codes and IETF language tags
	pub morx: Option<&'a [u8]>, // extended glyph metamorphosis table
	pub opbd: Option<&'a [u8]>, // optical bounds table
	pub prop: Option<&'a [u8]>, // glyph properties table
	pub trak: Option<&'a [u8]>, // tracking table
	pub zapf: Option<&'a [u8]>, // information about individual glyphs
	// OpenType Font Variations
	pub avar: Option<&'a [u8]>, // Axis variations
	pub cvar: Option<&'a [u8]>, // CVT variations (TrueType only)
	pub fvar: Option<&'a [u8]>, // Font variations
	pub gvar: Option<&'a [u8]>, // Glyph variations (TrueType only)
	pub hvar: Option<&'a [u8]>, // Horizontal metrics variations
	pub mvar: Option<&'a [u8]>, // Metrics variations
	pub vvar: Option<&'a [u8]>, // Vertical metrics variations
	// Other OpenType table
	pub ltsh: Option<&'a [u8]>, //  Linear Threshold
	pub merg: Option<&'a [u8]>, //  Merge Table
	pub pclt: Option<&'a [u8]>, //  PCL 5 Table
	// Other Truetype table
	pub fond: Option<&'a [u8]>, // contents to old 'FOND' and 'NFNT'
	pub gcid: Option<&'a [u8]>, // data to map characters in the font to CIDs
	pub xref: Option<&'a [u8]>, // used by ftxdumperfuser and ftxenhancer
	// Other
	pub fftm: Option<&'a [u8]>, // FontForge timestamp
}

impl<'a> RawTables<'a> {
	pub fn parse(data: &'a [u8], records: LazyArray16<'a, TableRecord>) -> Self {
		let mut tables = Self::default();
		println!("====");
		for record in records {
			println!("{:?}", record);
			let start = record.offset as usize;
			let Some(end) = start.checked_add(record.length as usize) else {
				continue;
			};
			let table_data = data.get(start..end);
			// TODO check_sum
			match &record.tag.to_bytes() {
				b"head" => tables.head = table_data,
				b"maxp" => tables.maxp = table_data,
				b"fmtx" => tables.fmtx = table_data,

				b"hhea" => tables.hhea = table_data,
				b"hmtx" => tables.hmtx = table_data,
				b"hdmx" => tables.hdmx = table_data,

				b"vhea" => tables.vhea = table_data,
				b"vmtx" => tables.vmtx = table_data,
				b"VDMX" => tables.vdmx = table_data,

				b"cmap" => tables.cmap = table_data,

				b"name" => tables.name = table_data,
				b"OS/2" => tables.os_2 = table_data,
				b"post" => tables.post = table_data,
				b"meta" => tables.meta = table_data,
				b"fdsc" => tables.fdsc = table_data,
				b"STAT" => tables.stat = table_data,
				b"DSIG" => tables.dsig = table_data,
				// Glyphs TrueType Outlines
				b"cvt " => tables.cvt_ = table_data,
				b"fpgm" => tables.fpgm = table_data,
				b"glyf" => tables.glyf = table_data,
				b"loca" => tables.loca = table_data,
				b"prep" => tables.prep = table_data,
				b"gasp" => tables.gasp = table_data,
				// Glyphs CFF Outlines
				b"CFF " => tables.cff1 = table_data,
				b"CFF2" => tables.cff2 = table_data,
				b"VORG" => tables.vorg = table_data,
				// Glyphs SVG Outlines
				b"SVG " => tables.svg_ = table_data,
				// Glyphs Bitmap
				b"bdat" => tables.bdat = table_data,
				b"bloc" => tables.bloc = table_data,
				b"bhed" => tables.bhed = table_data,
				b"CBDT" => tables.cbdt = table_data,
				b"CBLC" => tables.cblc = table_data,
				b"COLR" => tables.colr = table_data,
				b"CPAL" => tables.cpal = table_data,
				b"EBDT" => tables.ebdt = table_data,
				b"EBLC" => tables.eblc = table_data,
				b"EBSC" => tables.ebsc = table_data,
				b"sbix" => tables.sbix = table_data,
				// Opentype Layout GGG
				b"BASE" => tables.base = table_data,
				b"GDEF" => tables.gdef = table_data,
				b"GPOS" => tables.gpos = table_data,
				b"GSUB" => tables.gsub = table_data,
				b"JSTF" => tables.jstf = table_data,
				b"MATH" => tables.math = table_data,
				// Apple Advanced Typography AAT
				b"acnt" => tables.acnt = table_data,
				b"ankr" => tables.ankr = table_data,
				b"bsln" => tables.bsln = table_data,
				b"feat" => tables.feat = table_data,
				b"just" => tables.just = table_data,
				b"kern" => tables.kern = table_data,
				b"kerx" => tables.kerx = table_data,
				b"lcar" => tables.lcar = table_data,
				b"ltag" => tables.ltag = table_data,
				b"morx" => tables.morx = table_data,
				b"opbd" => tables.opbd = table_data,
				b"prop" => tables.prop = table_data,
				b"trak" => tables.trak = table_data,
				b"Zapf" => tables.zapf = table_data,
				// OpenType Font Variations
				b"avar" => tables.avar = table_data,
				b"cvar" => tables.cvar = table_data,
				b"fvar" => tables.fvar = table_data,
				b"gvar" => tables.gvar = table_data,
				b"HVAR" => tables.hvar = table_data,
				b"MVAR" => tables.mvar = table_data,
				b"VVAR" => tables.vvar = table_data,
				// Other OpenType table
				b"LTSH" => tables.ltsh = table_data,
				b"MERG" => tables.merg = table_data,
				b"PCLT" => tables.pclt = table_data,
				// Other Truetype table
				b"fond" => tables.fond = table_data,
				b"gcid" => tables.gcid = table_data,
				b"xref" => tables.xref = table_data,
				// Other
				b"FFTM" => tables.fftm = table_data,
				_ => println!("unkonw table: {:?}", record.tag),
			}
		}
		tables
	}
}

/// A rectangle.
///
/// Doesn't guarantee that `x_min` <= `x_max` and/or `y_min` <= `y_max`.
#[repr(C)]
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Rect {
	pub x_min: i16,
	pub y_min: i16,
	pub x_max: i16,
	pub y_max: i16,
}
impl Rect {
	#[inline]
	fn zero() -> Self {
		Self {
			x_min: 0,
			y_min: 0,
			x_max: 0,
			y_max: 0,
		}
	}
	/// Returns rect's width.
	#[inline]
	pub fn width(&self) -> i16 {
		self.x_max - self.x_min
	}
	/// Returns rect's height.
	#[inline]
	pub fn height(&self) -> i16 {
		self.y_max - self.y_min
	}
}

/// A rectangle described by the left-lower and upper-right points.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RectF {
	/// The horizontal minimum of the rect.
	pub x_min: f32,
	/// The vertical minimum of the rect.
	pub y_min: f32,
	/// The horizontal maximum of the rect.
	pub x_max: f32,
	/// The vertical maximum of the rect.
	pub y_max: f32,
}
impl RectF {
	#[inline]
	pub fn new() -> Self {
		RectF {
			x_min: f32::MAX,
			y_min: f32::MAX,
			x_max: f32::MIN,
			y_max: f32::MIN,
		}
	}
	#[inline]
	pub fn is_default(&self) -> bool {
		self.x_min == f32::MAX && self.y_min == f32::MAX && self.x_max == f32::MIN && self.y_max == f32::MIN
	}
	#[inline]
	pub fn extend_by(&mut self, x: f32, y: f32) {
		self.x_min = self.x_min.min(x);
		self.y_min = self.y_min.min(y);
		self.x_max = self.x_max.max(x);
		self.y_max = self.y_max.max(y);
	}
	#[inline]
	pub fn to_rect(self) -> Option<Rect> {
		Some(Rect {
			x_min: i16::try_num_from(self.x_min)?,
			y_min: i16::try_num_from(self.y_min)?,
			x_max: i16::try_num_from(self.x_max)?,
			y_max: i16::try_num_from(self.y_max)?,
		})
	}
}

/// An affine transform.
#[derive(Clone, Copy, PartialEq)]
pub struct Transform {
	/// The 'a' component of the transform.
	pub a: f32,
	/// The 'b' component of the transform.
	pub b: f32,
	/// The 'c' component of the transform.
	pub c: f32,
	/// The 'd' component of the transform.
	pub d: f32,
	/// The 'e' component of the transform.
	pub e: f32,
	/// The 'f' component of the transform.
	pub f: f32,
}
impl Transform {
	/// Creates a new transform with the specified components.
	#[inline]
	pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
		Transform { a, b, c, d, e, f }
	}
	/// Creates a new translation transform.
	#[inline]
	pub fn new_translate(tx: f32, ty: f32) -> Self {
		Transform::new(1.0, 0.0, 0.0, 1.0, tx, ty)
	}
	/// Creates a new rotation transform.
	#[inline]
	pub fn new_rotate(angle: f32) -> Self {
		let cc = (angle * core::f32::consts::PI).cos();
		let ss = (angle * core::f32::consts::PI).sin();

		Transform::new(cc, ss, -ss, cc, 0.0, 0.0)
	}
	/// Creates a new skew transform.
	#[inline]
	pub fn new_skew(skew_x: f32, skew_y: f32) -> Self {
		let x = (skew_x * core::f32::consts::PI).tan();
		let y = (skew_y * core::f32::consts::PI).tan();

		Transform::new(1.0, y, -x, 1.0, 0.0, 0.0)
	}
	/// Creates a new scale transform.
	#[inline]
	pub fn new_scale(sx: f32, sy: f32) -> Self {
		Transform::new(sx, 0.0, 0.0, sy, 0.0, 0.0)
	}
	/// Combines two transforms with each other.
	#[inline]
	pub fn combine(ts1: Self, ts2: Self) -> Self {
		Transform {
			a: ts1.a * ts2.a + ts1.c * ts2.b,
			b: ts1.b * ts2.a + ts1.d * ts2.b,
			c: ts1.a * ts2.c + ts1.c * ts2.d,
			d: ts1.b * ts2.c + ts1.d * ts2.d,
			e: ts1.a * ts2.e + ts1.c * ts2.f + ts1.e,
			f: ts1.b * ts2.e + ts1.d * ts2.f + ts1.f,
		}
	}
	#[inline]
	fn apply_to(&self, x: &mut f32, y: &mut f32) {
		let tx = *x;
		let ty = *y;
		*x = self.a * tx + self.c * ty + self.e;
		*y = self.b * tx + self.d * ty + self.f;
	}
	/// Checks whether a transform is the identity transform.
	#[inline]
	pub fn is_default(&self) -> bool {
		// A direct float comparison is fine in our case.
		self.a == 1.0 && self.b == 0.0 && self.c == 0.0 && self.d == 1.0 && self.e == 0.0 && self.f == 0.0
	}
}
impl Default for Transform {
	#[inline]
	fn default() -> Self {
		Transform::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
	}
}
impl core::fmt::Debug for Transform {
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "Transform({} {} {} {} {} {})", self.a, self.b, self.c, self.d, self.e, self.f)
	}
}
